<?php
namespace app\controller\admin\module\menu\components;

use app\components\traits\Singleton;

class Position
{
    use Singleton;
    protected string $modelName = 'menu';
    private int $maxLevel = 3;  //多级分叉树的最大深度，这里最大深度/菜单等级最大值 3
    /**
     * 操作自增位置
     * @param array $info
     * @param int $num
     * @return array
     */
    public function inc(array $info, int $num = 2): array
    {
        if(!$this->model->editInc([
            ['lef', '>', $info['righ']]
        ], 'lef', $num)){
            return responseArrayMsg('位置更新失败!!!!');
        }
        if(!$this->model->editInc([
            ['lef', '>', $info['righ']]
        ], 'righ', $num)){
            return responseArrayMsg('位置更新失败!!!!!');
        }
        try {
            $this->updateParent($info, 1, $num);
            return responseArrayMsg('ok', 200);
        } catch (\Exception $e) {
            return responseArrayMsg($e->getMessage());
        }
    }

    /**
     * 操作自减位置
     * @param array $info
     * @param int $num
     * @return array
     */
    public function dec(array $info, int $num = 2): array
    {
        //先将其他菜单的位置调整
        if (!$this->model->editDec([
            ['lef', '>', $info['righ']]
        ], 'lef', $num)) {
            return responseArrayMsg('位置更新失败!');
        }
        if (!$this->model->editDec([
            ['lef', '>', $info['righ']]
        ], 'righ', $num)) {
            return responseArrayMsg('位置更新失败!!');
        }
        $oldParentInfo = $this->model->getInfo(['id' => $info['pid']], 'id, lef, righ');
        if(empty($oldParentInfo)){
            return responseArrayMsg('未找到菜单信息');
        }
        //将父级菜单的位置调整
        try {
            $this->updateParent($oldParentInfo, 2, $num);
            return responseArrayMsg('ok', 200);
        } catch (\Exception $e) {
            return responseArrayMsg($e->getMessage());
        }
    }

    /**
     * 更新父级的Righ
     * @param array $parentInfo
     * @param int $mode
     * @param int $num
     * @throws \Exception
     */
    private function updateParent(array $parentInfo, int $mode = 1, int $num = 2){
        $grandpaInfo = $this->model->getInfo([
            ['lef', '<', $parentInfo['lef']],
            ['righ', '>', $parentInfo['righ']],
        ], 'id, lef, righ, name', ['righ' => 'asc']);
        $res = $mode === 1 ? $this->model->editInc(['id' => $parentInfo['id']], 'righ', $num) : $this->model->editDec(['id' => $parentInfo['id']], 'righ', $num);
        if(!$res){
            throw new \Exception('更新父级失败!!!');
        }
        !empty($grandpaInfo) and $this->updateParent($grandpaInfo, $mode);
    }


    /**
     * 重置lef和righ位置
     * @return void
     */
    public function reload(){
        $list = $this->model->getList([], 'id, lef, righ, level, is_home_page, pid, sort, name');
        array_multisort(array_column($list, 'level'), SORT_ASC, array_column($list, 'is_home_page'), SORT_DESC, array_column($list, 'sort'), SORT_DESC, $list);
//        echo '<pre>';
        $list = $this->buildTree(infiniteCategory($list,0 ,0));
        array_multisort(array_column($list, 'is_home_page'), SORT_DESC, array_column($list, 'lef'), SORT_ASC, array_column($list, 'level'), SORT_ASC,    array_column($list, 'sort'), SORT_DESC, $list);
        foreach($list as $v){
            $this->model->edit(['id' => $v['id']], $v);
        }
        putAdminLog('重新加载菜单的Lef和Righ信息');
//        echo "<hr/>";
//        echo "<hr/>";
//        echo "<hr/>";
//        echo "<hr/>";
//        echo "<hr/>";
//        print_r($list);
//        return responseMsg('操作成功', 200);
    }

//    示例数据
//      id      lef         righ        pid         level   name
//      1       1	        86	        0	        0	    顶级菜单
//      2       86	        87	        1	        1	    管理员管理
//      3       87	        88	        2	        2	    管理员列表
//      4       12	        13	        3	        3	    列表
//      5       14	        15	        3	        3	    添加页面
    private function buildTree(array $list, int $pid = 1, int $level = 1): array
    {
        $array = [];
        static $left = 1;
        static $right;
        static $processed = [];
        $pidx = array_filter(array_unique(array_column($processed, 'pid')));
        foreach($list as $v){
            if(in_array($v['id'], $pidx) or ($v['pid'] > 0 and $v['pid'] != $pid)){  //$v['pid'] 第一次会等于0
                continue;
            }
            if($right > 0 and $left <= $right){ //如果right已经被赋值为数字了，那么此时left和right相等，必须要left比right大1，否则会出现bug
                $left = $right + 1;
            }
//            $array[] = $v;  //将当前等级的数据加入暂存点
            $processed[] = $v;  //存入已处理过的数据
            $v['lef'] = $left;
            $left++;
            if($level < $this->maxLevel){
                if($level == $v['level']){  //$v['level'] 第一次会等于0
                    $child = $this->buildTree(multi_array_diff($list, $processed), $v['id'], $level + 1);
                    if(!empty($child)){ //如果有值，那么说明$right被更新过，那么递归处理完之后，应该再次更新$right
                        $right++;
                    }else{  //没有值的情况，$right并没有被更新，所以直接使用已经 +1的$left的值
                        $right = $left;
                    }
                    $v['righ'] = $right;    //等待递归处理完，才能赋值righ，栈内会将$right赋值更新，所以需要等待
                    !empty($child) and $array = array_merge($array, $child);
                }
            }else{
                $right = $left;
                $v['righ'] = $right;
            }
            $array[] = $v;  //将当前等级的数据加入暂存点
        }
        $level == 1 and $array[0]['righ'] = $right + 1;
        return $array;
    }
}